# -*- coding: utf-8 -*-
import sys
import os
import json
import time
import redis
from threading import Timer
import socket
import urllib
import platform
import datetime
import requests
import subprocess
import traceback
import random
import schedule
import threading
from requests import Response
from requests.structures import CaseInsensitiveDict
from requests.cookies import cookiejar_from_dict
from i_util.logs import LOG_DIRECTORY
from i_util.tools import get_tld
from requests.exceptions import ProxyError
from requests.exceptions import HTTPError
DEADLINE = 3600
class PhantomResponse(Response):
    def __init__(self,attr_dict=None):
        super(PhantomResponse, self).__init__()
        self.js_script_result = None
        if attr_dict:
            self.attr_from_dict(attr_dict)

    def attr_from_dict(self, dict):
        self.status_code = dict['status_code']
        self.url = dict['url']
        self._content = dict['content']
        self.js_script_result = dict.get('js_script_result')
        self.elapsed = datetime.timedelta(seconds=dict['time'])
        self.cookies = cookiejar_from_dict(dict['cookies'])
        self.headers = CaseInsensitiveDict(dict['headers'])


class PhantomDownloader(object):
    default_body = {
        'url':'',
        'method': 'get',
        'headers': {
            'Connection': 'close'
        },
        'allow_redirects': True,
        'use_gzip': True,
        'proxy':{},
        'timeout':60,
        'priority': 0,
        'verify': False,
        'download_type': 'phantom',
        'check_size': 1000,
        # 'timeout': 60,
        # 'viewport_width': 1024,
        # 'viewport_height': 768,
        # 'js_script': '!@#$%',
        # 'js_run_at': 'document',

        # 'load_images': False,
        # 'load_css': False,
        # 'wait_before_end': 1,
    }

    def __init__(self, conf,script_path=None, log_path=None):
        self.conf=conf
        self.log=conf.get('log')
        self.servers = []
        if not script_path:
            script_path = '%s/downloader/phantom.js' % os.getcwd()
        self.script_path = script_path
        if not log_path:
            log_path = "%sphantom_server.log" % LOG_DIRECTORY
        self._log = open(log_path, 'w')
        self.create_server_pool()
        self.phanres=threading.Thread(target=self.phanrestart,args=(self.conf.get('phantomjsrestart'),))
        self.phanres.start()
        self.lastrun=time.time()


    def __del__(self):
        for server in self.servers:
            self.stop_server(server['process'])

    def phanrestart(self,phantomjsrestart):
        while True:
            time.sleep(100)
            if time.time()-self.lastrun > phantomjsrestart:
                self.lastrun=time.time()
                self.restart()


    def restart(self):
        self.__del__()
        self.servers = []
        self.create_server_pool()
    def create_server_pool(self):
        for i in range(0, self.conf.get('phantomjs_num')):
            self.servers.append(self.create_server())

    def create_server(self, proxy=None):
        process = None
        for i in range(3):
            try:
                port = self.free_port()
                process = self.start_server(port, proxy=proxy)
                now = time.time()
                break
            except Exception as e:
                self.log.error(e)
        if process is None:
            return
        server = {}
        server['process'] = process
        server['server_addr'] = 'http://127.0.0.1:%s' % port
        server['proxy'] = proxy
        server['birth'] = now
        server['stat'] = 0
        return server

    def start_server(self, port, proxy=None):
        args = []
        args.append(self.conf.get('phantomjs_path'))
        args.append('--load-images=false')
        if proxy:
            args.append('--proxy=%s:%s' % (proxy['host'], proxy['port']))
            args.append('--proxy-auth=%s:%s' % (proxy['username'], proxy['password']))
        args.append(self.script_path)
        args.append(str(port))
        args.append('steady')
        try:
            process = subprocess.Popen(' '.join(args), shell=True,
                                       stdin=subprocess.PIPE, close_fds=platform.system() != 'Windows',
                                        stdout=self._log, stderr=self._log)

        except Exception as e:
            raise Exception("Unable to start phantomjs %s: %s" % (port, e))
        count = 0
        while not self.is_connectable(port):
            count += 1
            time.sleep(1)
            if count == 30:
                 raise Exception(
                     "Can not connect to Phantom Server on port {}".format(port))
        return process

    def stop_server(self, process):
        """
        Cleans up the process
        """
        #If its dead dont worry
        if process is None:
            return

        #Tell the Server to properly die in case
        try:
            if process:
                process.stdin.close()
                process.kill()
                process.wait()
        except OSError:
            # kill may not be available under windows environment
            pass

    def free_port(self):
        """
        Determines a free port using sockets.
        """
        free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        free_socket.bind(('0.0.0.0', 0))
        free_socket.listen(5)
        port = free_socket.getsockname()[1]
        free_socket.close()
        return port

    def is_connectable(self, port):
        """
        Tries to connect to the server at port to see if it is running.

        :Args:
         - port: The port to connect.
        """
        try:
            socket_ = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            socket_.settimeout(1)
            socket_.connect(("127.0.0.1", port))
            result = True
        except socket.error:
            result = False
        finally:
            socket_.close()
        return result

    def get_server(self):
        return self.servers[random.randint(0, sys.maxint) % len(self.servers)]

    def package_body(self, url, **kwargs):
        body = self.default_body.copy()
        body['headers']=kwargs['http_header']
        body.update(kwargs)
        body['url'] = url
        if body['method'] == 'post':
            try:
                body['data'] = urllib.urlencode(body['post_data'])
            except:
                traceback.format_exc()
        for k, v in body.items():
            if isinstance(v, dict):
                body[k] = json.dumps(v)
        return body

    def package_response(self, res):
        response = PhantomResponse(json.loads(res.content))
        return response

    def proxy_package(self,proxy,kw):
        if not proxy:
            return
        kw['host']=proxy.host
        kw['password']=proxy.password
        kw['user']=proxy.user
        kw['port']=str(proxy.port)


    def req_to_kw(self,req,kw):
        for k, v in self.default_body.items():
            if hasattr(req,k) and getattr(req,k)!=None:
                kw[k] = getattr(req,k)
            else:
                kw[k]=v
        if req.method=='post':
            kw['data'] = urllib.urlencode(req.post_data)

        if kw.get('data','')=='':
            kw['data']='{}'
        kw['headers'] = req.http_header
        kw['timeout'] = req.time_out
        proxy={}
        try:
            kw['domain']=get_tld(req.url)
        except:
            kw['domain']=req.url.split('/')[2]
            self.log.error('phantom\tdownload\turl:%s\tget doamin fail'%(req.url))
        self.proxy_package(req.proxy,proxy)
        kw['proxy']=proxy
        for k, v in kw.items():
            if isinstance(v, dict):
                kw[k] = json.dumps(v)
    def download(self, req):
        kwargs_ = {}
        server = self.get_server()
        if not server:
            self.log.error('Server not exist! url: %s' % req.url)
            return None
        server_addr = server['server_addr']
        #body = self.package_body(kw['url'],kw)
        self.req_to_kw(req, kwargs_)
        try:
            response = requests.post(server_addr, data=kwargs_, timeout=kwargs_['timeout'])
            response = self.package_response(response)
            if response.status_code in [403, 599, 500, 502, 503, 504, 400, 408]:
                req.identify_status = 7
            # if response.status_code in [403, 599, 500, 502, 503, 504, 400, 408]:
            #     raise Exception(response.reason)
            return response
        except ProxyError as e:
            req.identify_status = 8
            self.log.error('simple_download\turl:%s\tproxy:%s\tping\tfail' % (req.url, kwargs_['proxies']))
        except HTTPError or requests.ConnectTimeout or requests.ConnectionError as e:
            req.identify_status = 7
            self.log.error('simple_download\turl:%s\tmethod:%s\tproxy\tfail' % (req.url, kwargs_['proxies']))
        except Exception as e:
            self.log.error('phantom_downlaod\tfail\treason%s'%e)
            return None
